#!/usr/bin/env php
<?php

/**
 * 仁言利溥，至堪钦佩
 * * 专门为 windows 系统提供了常驻内存运行启动脚本 *
 * ----------------------------------------------
 * 仅为开发时修改文件免去重启的麻烦，不要用于生产环境
 * ----------------------------------------------
 * TODO: 运行启动命令：php dev
 */

error_reporting(E_ALL);
ini_set('display_errors', 'on');
ini_set('memory_limit', '256M');

if (DIRECTORY_SEPARATOR === '\\') {
    // see https://www.php.net/manual/zh/pcntl.constants.php
    !defined('SIGUSR1') && define('SIGUSR1', 10);
}

/** 命令行参数 */
$process_files = [PHP_BINARY, '-S 0.0.0.0:8800 -t ./public/'];

/** 设定监控热更新的目录 */
$_paths = [
    __DIR__ . '/addons/',
    __DIR__ . '/application/',
    __DIR__ . '/extend/',
    __DIR__ . '/public/',
    __DIR__ . '/.env',
];

/** 
 * 监控文件是否更新过而杀句柄进程
 */
function checkFilesChange($monitor_dir)
{
    static $last_mtime;
    if (!$last_mtime) {
        $last_mtime = time();
    }

    clearstatcache();

    if (!is_dir($monitor_dir)) {
        if (!is_file($monitor_dir)) {
            return;
        }
        $iterator = [new \SplFileInfo($monitor_dir)];
    } else {
        // *递归遍历目录
        $dir_iterator = new \RecursiveDirectoryIterator($monitor_dir, \FilesystemIterator::FOLLOW_SYMLINKS);
        $iterator = new \RecursiveIteratorIterator($dir_iterator);
    }

    foreach ($iterator as $file) {
        // *仅监控文件
        if (is_dir($file)) {
            continue;
        }
        // *检查文件是否修改
        if ($last_mtime < $file->getMTime() && in_array($file->getExtension(), ['php', 'twig', 'html', 'htm', 'env', 'xml', 'yaml'], true)) {
            $var = 0;
            // *php -l 自动检测文件是否有语法错误
            if ($file->getExtension() === 'php') {
                exec(PHP_BINARY . " -l " . $file, $out, $var);
            }
            if ($var) {
                // *有语法错误，仅记下修改时间不杀进程
                $last_mtime = $file->getMTime();
                echo "+++++++++++++++++++++++++++++++++++++++++++++++\r\n";
                echo "✘ 请留意上方PHP语法错误异常警告 \r\n";
                echo "+++++++++++++++++++++++++++++++++++++++++++++++\r\n";
                continue;
            }
            $last_mtime = $file->getMTime();
            echo $file . " update and reload server\r\n";

            return true;
        }
    }
    return;
}

/** 
 * 检查目录是否更新过文件
 */
function checkAllFilesChange($_paths)
{
    foreach ($_paths as $path) {
        if (checkFilesChange($path)) {
            return true;
        }
    }
    return false;
}

/** 
 * 运行命令行，并获取命令行句柄进程
 */
function popen_processes($process_files)
{
    $cmd = implode(' ', $process_files);
    $descriptorspec = [STDIN, STDOUT];
    $resource = proc_open($cmd, $descriptorspec, $pipes);
    if (!$resource) {
        exit("Can not execute $cmd\r\n");
    }
    return $resource;
}

/**
 * 通过句柄获取当前进程PID(Server PID)
 */
function popen_processes_pid($resource)
{
    $status = proc_get_status($resource);
    return $status['pid'] ?? 0;
}

$resource = popen_processes($process_files);
$server_pid = popen_processes_pid($resource);
// echo '> The PHP PID: ' . posix_getppid() . "\r\n";
echo '> Run Server PID: ' . $server_pid . "\r\n";
echo "+++++++++++++++++++++++++++++++++++++++++++++++\r\n";
echo "> 此启动模式仅限于开发环境，切勿用于生产环境！！\r\n";
echo "+++++++++++++++++++++++++++++++++++++++++++++++\r\n";

while (true) {
    sleep(1); // 长驻内存运行模式的热更新监控时间为每隔一秒
    if (checkAllFilesChange($_paths)) {
        if ($server_pid) {
            if (function_exists('posix_kill')) {
                posix_kill($server_pid, SIGUSR1);
            } else if (function_exists('shell_exec')) {
                // *适用于 windows 杀 Server PID
                shell_exec("taskkill /F /T /PID $server_pid");
            } else {
                echo "Killing process failed, please restart manually\r\n";
            }
            proc_close($resource); // 关闭进程

            $resource = popen_processes($process_files);
            $server_pid = popen_processes_pid($resource);
        }
    }
}
